package com.cskaoyan.javase.oop2._2polymorphic._2advantage;

/**
 * 多态是面向对象三大特征之一，它基于继承而实现，是面向对象思想的集大成者。接下来我们研究一下，它的优缺点。我们通过一个案例：
 * 科学家要收集动物的叫声去研究，先要收集猫，狗两种动物的叫声研究。随后又扩展到其它动物，最后又需要研究人类。
 *
 *
 * 我们写一个方法,形参数据类型写父类类型,在方法调用时,传入具体的子类类型,从而具有多态现象.
 *
 * 从这个案例，我们其实就可以大致总结多态的优点了：
 * 要实现多态必须要继承，而继承提高了代码复用率，提升了程序的可维护性。（继承保证）
 * 有了多态后，用父类引用指向不同子类对象，只需要调用同名方法，就能自动根据不同子类对象，得出不同的行为。
 * 这大幅度简化了代码，提升了程序的扩展性。（多态保证）
 *
 * 现在科学家已经研究完毕所有动物,于是开始研究人类.要想研究人类,让人类配合,得给钱,给钱才叫!
 * 所以人类这个动物的子类,有自身独有的方法,叫—加钱
 *
 * 引用数据类型强制类型转换(向下转型)的语法:
 * 父类引用 ---> 子类引用
 * 语法:
 * 子类引用的类名 对象名 =  (子类引用的类名)父类引用
 *
 * 1.向下转型能够通过编译的条件是什么?
 *      要想通过编译,只要强转成子类引用就可以,无论什么子类都行.
 *      但是如果不是强转成子类,是不能通过编译的
 *
 * 2.向下转型能够运行成功的条件是什么?
 *      要想强转成功,强转后的引用必须是真的能够指向这个对象的引用
 *      可以是这个对象的数据类型本身的引用
 *      也可以是这个对象的数据类型父类的引用(父类引用可以指向子类对象)
 *
 * 引用数据类型的强转类型转换是很危险的,一旦强转失败:
 * 会导致程序抛出ClassCastException异常(类型转换异常),程序终止.
 * 所以在做向下转型时,一定要深思熟虑,不要瞎写代码做强转.
 *
 * 强转很危险,一方面我们需要一个语法来判断究竟何时能够做强转,另一方面我们也需要一个语法来规避不合理的强转,于是Java引入了instanceof关键字
 * 语法:
 *      引用 instanceof 类名
 * 表示判断引用所指向的对象,是否是后面类名的对象或者子类对象,语法结构会返回一个布尔值,true表示是,false表示不是
 *      很明显,当该语法结构返回true时,能够做强转
 *      返回false时,不能做强转
 *      特殊的,当引用指向null时,直接返回false.
 *
 * instanceof多和if一起使用,用来判断强转.实际开发中,建议在做强转之前,永远使用instanceof判断一下
 *      除非有万全的把握不会出错.
 *
 *
 * @since 09:32
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // collectCatVoice(new Cat());
        /*
            收集研究动物的叫声
            父类引用指向不同的子类对象
            调用子类中重写的同名成员方法
            根据传入子类对象的不传,体现出不同的行为
            这就是多态
          */
        collectAnimalVoice(new Cat());
        collectAnimalVoice(new Dog());
        collectAnimalVoice(new Pig());

       collectAnimalVoice(new Person());
    }

    /*// 收集研究猫的叫声
    public static void collectCatVoice(Cat c) {
        // 抓一只猫过来,让它叫
        c.shout();
        // 收集研究
    }*/

    // 收集研究动物的叫声
    public static void collectAnimalVoice(Animal a) {
        /*
            人这种动物比较特殊,需要先加钱,再叫
            在这里,引用a能够访问子类独有方法吗?
            不行
            因为对象名点成员方法的访问范围看引用
            父类引用这时就限制了子类对象的功能
            为了访问子类独有成员,需要将父类引用转换成子类引用
            这属于"向下转型",是强制类型转换

            为了更好的在a引用指向Person对象时做强转
            需要使用instanceof判断
         */
        if (a instanceof Person){
            // 可以强转
            Person p = (Person) a;
            p.addMoney();
        }

        // 抓一只动物过来,让它叫
        a.shout();
        // 收集研究
    }
}

class Animal {
    String name;
    int age;

    public void shout() {
        System.out.println("我在叫!");
    }
}

class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("喵喵喵!");
    }
}

class Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("汪汪汪!");
    }
}

class Pig extends Animal {
    @Override
    public void shout() {
        System.out.println("哼哼哼!");
    }
}

class Person extends Animal {
    @Override
    public void shout() {
        System.out.println("嘤嘤嘤！");
    }

    // 子类独有的方法
    public void addMoney() {
        System.out.println("很润,得加钱！");
    }
}